package com.dataart.training.crimequalification.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.ObjectRetrievalFailureException;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;

public class PasswordEncoder implements org.springframework.security.authentication.encoding.PasswordEncoder {

    //-----------------------------------------------------------------------------------

    private static final Logger logger = LoggerFactory
            .getLogger(PasswordEncoder.class);

    //-----------------------------------------------------------------------------------

    /**
     * Encodes the specified raw password with an implementation specific algorithm.
     * If a salt value is provided, the same salt value must be use when calling the isPasswordValid(String, String, Object) method.
     * Note that a specific implementation may choose to ignore the salt value (via null), or provide its own.
     *
     * @param rawPass - the password to encode
     * @param salt    - optionally used by the implementation to "salt" the raw password before encoding. A null value is legal.
     * @return encoded password
     * @throws DataAccessException
     */
    @Override
    public String encodePassword(String rawPass, Object salt) throws DataAccessException {

        try {
            String saltStr = (String) salt;
            return encrypt(rawPass, saltStr);
        } catch (Exception e) {
            throw new DataAccessResourceFailureException("Failed to encode password.", e);
        }

    }

    /**
     * Validates a specified "raw" password against an encoded password.
     * The encoded password should have previously been generated by encodePassword(String, Object).
     * This method will encode the rawPass (using the optional salt), and then compared it with the presented encPass.
     *
     * @param encPass - a pre-encoded password
     * @param rawPass - a raw password to encode and compare against the pre-encoded password
     * @param salt    - optionally used by the implementation to "salt" the raw password before encoding. A null value is legal.
     * @return
     * @throws DataAccessException
     */
    @Override
    public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {

        try {
            return encrypt(rawPass, (String) salt).equals(encPass);
        } catch (UnsupportedEncodingException e) {
            throw new ObjectRetrievalFailureException("Encoding not supported.", e);
        } catch (NoSuchAlgorithmException e) {
            throw new ObjectRetrievalFailureException("Hashing algorithm not supported.", e);
        }
    }

    /**
     * Auxiliary method to encode password with specified algorithm.
     *
     * @param pass - the password to encode
     * @param salt - string to "salt" the raw password with before encoding
     * @return encoded password
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     */
    private String encrypt(String pass, String salt) throws UnsupportedEncodingException, NoSuchAlgorithmException {

        String saltPassStr = addSaltToPassword(pass, salt);
        Hasher hasher = new Hasher();
        return hasher.getSHAHash(saltPassStr);
    }

    private String addSaltToPassword(String password, String salt) {
        return password + salt;
    }

}